from xml.etree import ElementTree
#from elementtree import ElementTree # for Python 2.4 or lower
import web
import xml_dict

urls = (
    '/login', 'url_login',
    '/logout', 'url_logout',
    '/crud/(.*)', 'url_crud'
)
web.config.debug = False
app = web.application(urls, globals())
render = web.template.render('templates/')

use_session = False
if use_session:
    session = web.session.Session(app, web.session.DiskStore('sessions'), initializer={'loggedin': False})
    web.config.session_parameters['timeout'] = 60 * 3 # 3 minutes
    web.config.session_parameters['ignore_expiry'] = True
    web.config.session_parameters['ignore_change_ip'] = True

class FlexResponse:
    XML_HEADER = '<?xml version="1.0" encoding="ISO-8859-1"?>\n'
    def __init__(self, data, meta):
        self.data = data
        self.meta = meta

    def tostring(self):
        root = xml_dict.ConvertDictToXml({'response': {
            'data': self.data,
            'metadata': self.meta
        }})
        return self.XML_HEADER + ElementTree.tostring(root)

    def __str__(self):
        return self.tostring()

class FlexDatagridResponse(FlexResponse):
    def __init__(self, objects):
        FlexResponse.__init__(self, {'row': objects}, {})

class FlexError(FlexResponse):
    '<response><data><error>%s</error></data><metadata /></response>'
    ERR_NO_OPERATION = "No operation"
    ERR_NO_ROW_FOUND = "No row found"

    def __init__(self, error_msg):
        FlexResponse.__init__(self, {'error': error_msg}, {})

class FlexNoOperationError(FlexError):
    def __init__(self):
        FlexError.__init__(self, FlexError.ERR_NO_OPERATION)

class FlexNoRowFoundError(FlexError):
    def __init__(self):
        FlexError.__init__(self, FlexError.ERR_NO_ROW_FOUND)

class DataStore:
    def __init__(self): self.tablenames = []
    def hastable(self, tablename): return tablename in self.tablenames
    def findall(self, tablename, fltr='', orderField='', orderDirection=''): pass
    def insert(self, tablename, obj): pass
    def delete(self, tablename, k, v): pass
    def update(self, tablename, obj): pass

class MemoryDataStore(DataStore):
    def __init__(self, dataset):
        self.dataset = dataset
        self.tablenames = dataset.keys()

    def findall(self, tablename, fltr='', orderField='', orderDirection=''):
        if self.hastable(tablename):
            try: return self.dataset[tablename]
            except: return None
        else: return None # table not found

    def insert(self, tablename, obj):
        if self.hastable(tablename):
            try:
                obj['id'] = int(obj['id'])
                self.dataset[tablename].append(obj)
                return obj
            except: return None
        else: return None

    def delete(self, tablename, k, v):
        if self.hastable(tablename):
            for e in self.dataset[tablename]:
                if e[k] == int(v): # assume id is integer
                    self.dataset[tablename].remove(e)
                    return v
            return None
        else: return None

    def update(self, tablename, obj):
        if self.hastable(tablename):
            obj['id'] = int(obj['id'])
            for e in self.dataset[tablename]:
                if e['id'] == obj['id']: # assuming id is primary key and is integer
                    e = obj
                    return obj
            return None # not found
        else: return None

class url_login:
    def GET(self):
        if session.loggedin: raise web.seeother('/crud/')
        form = '''<html><body><form action="/login" method="post">
Username:<input type="text" name="user"><br>
Password:<input type="password" name="password"><br>
<input type="submit" value="Submit" />
</form></body></html>'''
        return form

    def POST(self):
        inp = web.input()
        if 'user' in inp.keys() and 'password' in inp.keys():
            if inp.user == 'admin' and inp.password == 'admin':
                session.loggedin = True
                session.user = inp.user
                raise web.seeother('/crud/')
            else: raise web.seeother('/login')
        else: raise web.seeother('/login')

class url_logout:
    def GET(self):
        session.kill()
        raise web.seeother('/login')

class url_crud:
    methods = ['FindAll', 'Insert', 'Delete', 'Update']

    store = MemoryDataStore({'employee':[
        { 'id':1, 'fname':'John', 'lname':'Smith', 'address':'1000 Wall St, New York City'},
        { 'id':2, 'fname':'Jenny', 'lname':'White', 'address':'2000 Main St, San Francisco'},
    ], 'department':[
        { 'id':1, 'name':'Sales'},
        { 'id':2, 'name':'Human Resources'},
    ]})

    def _findall(self, tablename, fltr, orderField, orderDirection):
        objects = self.store.findall(tablename, fltr, orderField, orderDirection)
        if objects: return str(FlexDatagridResponse(objects))
        else: return str(FlexNoOperationError())

    def _insert(self, tablename, obj):
        obj = self.store.insert(tablename, obj)
        if obj: return str(FlexDatagridResponse([obj]))
        else: return str(FlexNoOperationError())

    def _delete(self, tablename, k, v):
        v = self.store.delete(tablename, k, v)
        if v: return str(FlexResponse(v, {}))
        else: return str(FlexNoRowFoundError())

    def _update(self, tablename, obj):
        obj = self.store.update(tablename, obj)
        if obj: return str(FlexDatagridResponse([obj]))
        else: return str(FlexNoRowFoundError())

    def _get_table_list_page(self, tablename):
        return tablename

    def GET(self, tablename):
        if use_session:
            if not session.loggedin: raise web.seeother('/login')

        if not tablename or not self.store.hastable(tablename): # no table specified
            s = "<html><body>"
            for table in self.store.tablenames: s += '<a href="%s">%s</a><br>' % (table, table)
            if use_session and session.loggedin:
                logout = '<p><a href="/logout">Logout</a>'
            else: logout = ''
            return s + ' ' + logout + ' </body></html>'

        #return self.POST(tablename)
        return render.embed({'name': tablename})

    def POST(self, tablename):
        if use_session:
            if not session.loggedin: raise web.seeother('/login')

        inp = web.input()
        if 'method' in inp.keys() and inp.method in self.methods:
            if inp.method == 'FindAll':
                if 'filter' in inp.keys(): fltr = inp.filter
                else: fltr = ''
                if 'orderField' in inp.keys(): orderField = inp.orderField
                else: orderField = ''
                if 'orderDirection' in inp.keys(): orderDirection = inp.orderDirection
                else: orderDirection = ''
                return self._findall(tablename, fltr, orderField, orderDirection)
            elif inp.method == 'Insert':
                del inp['method'] # delete method arg, all other params belong to the object
                return self._insert(tablename, inp)
            elif inp.method == 'Delete':
                del inp['method'] # delete method arg, the only other param is the primaryKey
                pk = inp.keys()[0]
                return self._delete(tablename, pk, inp[pk])
            elif inp.method == 'Update':
                del inp['method'] # delete method arg, the only other param is the primaryKey
                return self._update(tablename, inp)
        else: return str(FlexNoOperationError())

if __name__ == "__main__":
    app.run()
